package cn.virens.web.components.mybatis;

import java.sql.SQLException;
import java.util.List;

import javax.sql.DataSource;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;

/**
 * 实现数据连接懒加载
 * 
 * @author virens
 */
public class VirExecutor implements Executor {
	private final TransactionFactory transactionFactory;
	private final TransactionIsolationLevel level;
	private final Configuration configuration;
	private final ExecutorType executorType;
	private final DataSource dataSource;

	private final boolean autoCommit;

	private Transaction transaction;
	private Executor executor;

	public VirExecutor(TransactionFactory transactionFactory, Configuration configuration, DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit, ExecutorType executorType) {
		this.transactionFactory = transactionFactory;
		this.configuration = configuration;
		this.executorType = executorType;
		this.autoCommit = autoCommit;
		this.dataSource = dataSource;
		this.level = level;
	}

	@Override
	public int update(MappedStatement ms, Object parameter) throws SQLException {
		return executor().update(ms, parameter);
	}

	@Override
	@SuppressWarnings("rawtypes")
	public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException {
		return executor().query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
	}

	@Override
	@SuppressWarnings("rawtypes")
	public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
		return executor().query(ms, parameter, rowBounds, resultHandler);
	}

	@Override
	public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
		return executor().queryCursor(ms, parameter, rowBounds);
	}

	@Override
	public List<BatchResult> flushStatements() throws SQLException {
		return executor().flushStatements();
	}

	@Override
	public void commit(boolean required) throws SQLException {
		executor().commit(required);
	}

	@Override
	public void rollback(boolean required) throws SQLException {
		executor().rollback(required);
	}

	@Override
	public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
		return executor().createCacheKey(ms, parameterObject, rowBounds, boundSql);
	}

	@Override
	public boolean isCached(MappedStatement ms, CacheKey key) {
		return executor().isCached(ms, key);
	}

	@Override
	public void clearLocalCache() {
		executor().clearLocalCache();
	}

	@Override
	public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
		executor().deferLoad(ms, resultObject, property, key, targetType);
	}

	@Override
	public Transaction getTransaction() {
		return executor().getTransaction();
	}

	@Override
	public void close(boolean forceRollback) {
		executor().close(forceRollback);
	}

	@Override
	public boolean isClosed() {
		return executor().isClosed();
	}

	@Override
	public void setExecutorWrapper(Executor executor) {
		executor().setExecutorWrapper(executor);
	}

	public void closeTransaction() {
		if (transaction == null) return;

		try {
			transaction.close();
		} catch (SQLException ignore) {
			// Intentionally ignore. Prefer previous error.
		}
	}

	private Executor executor() {
		if (transaction == null || executor == null) {
			this.transaction = transactionFactory.newTransaction(dataSource, level, autoCommit);
			this.executor = configuration.newExecutor(transaction, executorType);
		}

		return executor;
	}
}
